package com.randy.fundation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import androidx.annotation.NonNull;

public class JxFunc {
    public interface Convert<IN, OUT> {
        OUT yield(@NonNull IN value);
    }

    public interface Action<IN> {
        void yield(@NonNull IN value);
    }

    public interface Null {
        void yield();
    }

    public static class Nullable<IN> {
        IN value;

        public IN value() {
            return this.value;
        }

        Nullable(IN value) {
            this.value = value;
        }

        public Nullable<IN> ifPresent(Action<IN> op) {
            if (value != null && op != null) {
                op.yield(value);
            }
            return this;
        }


        public Nullable<IN> orNull(Null op) {
            if (value == null && op != null) {
                op.yield();
            }
            return this;
        }

        public Nullable<IN> orEmpty(Null op) {
            if (op == null) {
                op.yield();
                return this;
            }

            JxFunc.as(value, String.class).ifPresent(value -> {
                if (value.isEmpty()) {
                    op.yield();
                }
            }).orNull(() -> {
                if (isEmpty(value)) {
                    op.yield();
                }
            });

            return this;
        }

        /**
         * 映射到一个目标对象
         *
         * @param convert 转换器
         * @param <OUT>   目标对象类型
         * @return 目标对象, 可能为空
         */
        public <OUT> OUT flatMap(Convert<IN, OUT> convert) {
            if (value != null && convert != null) {
                return convert.yield(value);
            }
            return null;
        }

        public <OUT> Nullable<OUT> map(Convert<IN, OUT> op) {
            if (value != null && op != null) {
                return new Nullable<>(op.yield(value));
            }
            return new Nullable<>(null);
        }

        /**
         * 尝试将对象转换为目标类型
         *
         * @param <OUT> 目标类型
         * @return Reference对象
         */
        public <OUT> Nullable<OUT> as() {
            try {
                @SuppressWarnings("unchecked")
                OUT out = (OUT) value;
                return new Nullable<>(out);
            } catch (ClassCastException e) {
                // return
            }
            return new Nullable<>(null);
        }

        /**
         * 尝试将对象转换为目标类型
         *
         * @param clazz 目标类型
         * @param <OUT> 目标类型
         * @return Reference对象
         */
        public <OUT> Nullable<OUT> as(Class<OUT> clazz) {
            try {
                OUT out = clazz.cast(value);
                return new Nullable<>(out);
            } catch (ClassCastException e) {
                // return
            }
            return new Nullable<>(null);
        }
    }

    public static class IterableNullable<IN> {
        Iterable<IN> value;

        public Iterable<IN> value() {
            return this.value;
        }

        IterableNullable(Iterable<IN> value) {
            this.value = value;
        }

        public IterableNullable<IN> ifPresent(Action<IN> op) {
            if (value != null && op != null) {
                Iterator<IN> iterator = value.iterator();
                while (iterator.hasNext()) {
                    IN in = iterator.next();
                    op.yield(in);
                }
            }
            return this;
        }

        public IterableNullable<IN> orNull(Null op) {
            if (value == null && op != null) {
                op.yield();
            }
            return this;
        }

        public IterableNullable<IN> orEmpty(Null op) {
            boolean empty = (value == null || !value.iterator().hasNext());
            if (empty && op != null) {
                op.yield();
            }
            return this;
        }

        public <OUT> IterableNullable<OUT> map(Convert<IN, OUT> op) {
            if (value != null && op != null) {
                Iterator<IN> iterator = value.iterator();
                List<OUT>    outs     = new ArrayList<>();
                while (iterator.hasNext()) {
                    IN  in  = iterator.next();
                    OUT out = op.yield(in);
                    if (out != null) {
                        outs.add(out);
                    }
                }
                return new IterableNullable<>(outs);
            }
            return new IterableNullable<>(null);
        }

        public Nullable<Iterable<IN>> zip() {
            return new Nullable<>(value);
        }

        public Nullable<List<IN>> zipAsList() {
            return JxFunc.as(value);
        }
    }

    /**
     * 创建Nullable对象
     *
     * @param object 需要转换的对象
     * @param <T>    需要转换的对象类型
     * @return 非空的Nullable
     */
    public static <T> Nullable<T> of(T object) {
        return new Nullable<>(object);
    }

    /**
     * 创建IterableNullable对象, 用于迭代器
     *
     * @param object 需要转换的对象
     * @param <T>    需要转换的对象类型
     * @return 非空的Nullable
     */
    public static <T> IterableNullable<T> of(Collection<T> object) {
        return new IterableNullable<>(object);
    }

    public static <T> IterableNullable<T> of(T[] object) {
        return new IterableNullable<>(Arrays.asList(object));
    }

    /**
     * 尝试将对象转换为目标类型
     *
     * @param object 要转换的对象
     * @param <IN>   要转换的对象类型
     * @param <OUT>  目标类型
     * @return Reference对象
     */
    public static <IN, OUT> Nullable<OUT> as(IN object) {
        try {
            @SuppressWarnings("unchecked")
            OUT value = (OUT) object;
            return new Nullable<>(value);
        } catch (ClassCastException e) {
            // return
        }
        return new Nullable<>(null);
    }

    /**
     * 尝试将对象转换为目标类型
     *
     * @param object 要转换的对象
     * @param clazz  目标类型
     * @param <IN>   要转换的对象类型
     * @param <OUT>  目标类型
     * @return Reference对象
     */
    public static <IN, OUT> Nullable<OUT> as(IN object, Class<OUT> clazz) {
        try {
            OUT value = clazz.cast(object);
            return new Nullable<>(value);
        } catch (ClassCastException e) {
            // return
        }
        return new Nullable<>(null);
    }

    static <T> boolean isEmpty(Collection<T> obj) {
        return obj == null || obj.isEmpty();
    }

    static boolean isEmpty(String string) {
        return string == null || string.trim().isEmpty();
    }

    static boolean isEmpty(Object obj) {
        return obj == null || obj.toString().trim().isEmpty();
    }
}
